package com.newland.server.config;

import com.newland.server.model.SysUser;
import com.newland.server.service.SysUserService;
import com.newland.server.utils.CustomException;
import com.newland.server.utils.CustomExceptionType;
import com.newland.server.utils.JwtProperties;
import com.newland.server.utils.JwtTokenUtil;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.List;
import java.util.concurrent.TimeUnit;

public class CustomRealm extends AuthorizingRealm {

    @Autowired
    private SysUserService userService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    private JwtProperties jwtProperties;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 大坑，必须重写此方法，不然Shiro会报错
     */
    @Override
    public boolean supports(AuthenticationToken authenticationToken) {
        return authenticationToken instanceof CustomToken;
    }


    /**
     * 授权相关
     * @param principalCollection
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        String token = (String) principalCollection.getPrimaryPrincipal();//拿到token
        String principal = jwtTokenUtil.getPrincipalFromToken(token);//利用token工具类从token中解析出用户名

        List<String> apis = userService.findApis(principal);//根据用户名查询该用户可以看到的api
        simpleAuthorizationInfo.addStringPermissions(apis);//将这些api添加进授权中
        return simpleAuthorizationInfo;
    }

    /**
     * 认证身份
     * @param authenticationToken
     * @return
     * @throws
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String token = "";
        token = (String) authenticationToken.getPrincipal();//拿到token
        String oldToken=token;//现在还不知道当前token是否过期，将当前token用一个变量保存
        String principal = "";
        try {
            principal = jwtTokenUtil.getPrincipalFromToken(token);//从token中拿用户名
        } catch (Exception e) {
            e.printStackTrace();
            throw new CustomException(CustomExceptionType.USER_INPUT_ERROR,e.getMessage());
        }
        if(jwtTokenUtil.isTokenExpired(token)){//判断当前token是否过期
            if(stringRedisTemplate.opsForValue().get(principal)==null){//从redis中查找以用户名为键的值
                return null;
            }
//            oldToken=token;
            long currentTimeMillis = System.currentTimeMillis();
            try {
                token = jwtTokenUtil.refreshToken(token, currentTimeMillis);//进入这个方法表明当前token已经过期，需要刷新token
            } catch (Exception e) {
                e.printStackTrace();
                throw new CustomException(CustomExceptionType.SYSTEM_ERROR,e.getMessage());
            }
            stringRedisTemplate.opsForValue().set(principal,String.valueOf(currentTimeMillis),jwtProperties.getRedisexpiration(), TimeUnit.MILLISECONDS);//同步改变redis中的这个值,更新时间戳
        }
        SysUser user = userService.findUser(principal);
        if(user==null){
            throw new CustomException(CustomExceptionType.USER_INPUT_ERROR,"未知用户");
        }
        if(jwtTokenUtil.validateToken(token)){//请注意,这里是利用工具自己判断这个token是否合法,合法的话直接返回SimpleAuthenticationInfo
            return new SimpleAuthenticationInfo(oldToken,oldToken,getName());//这里一旦刷新了token，如果不更改就会拿老token和新token对比，自然不成功，要么重写底层方法，要么形式判断一下，真正不用它做判断，而是自己判断了的，只要它返回成功信号而已
        }else{
            return null;//认证不通过请重新登录
        }
    }
}
